//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Windows.Forms;
using Microsoft.TridentWordAddIn.Common;
using Microsoft.TridentWordAddIn.ResourceHelper;
using System.Threading;
using Microsoft.TridentWordAddIn.Server;

namespace Microsoft.TridentWordAddIn.Presentation
{
    public partial class RerunWorkflowTaskPane : BaseUserControl, ITaskPaneControl, IDisposable
    {
        private bool isCanceled, isError;
        private Thread asyncRerunJob;
        private Workflow workflow;
        private WorkflowJob job;
        private string exceptionMessage;
        private bool isRerunLaunched;
        private Guid jobId;
        private string jobName;

        /// <summary>
        /// Initializes a new instance of the <see cref="RerunWorkflowTaskPane"/> class.
        /// </summary>
        public RerunWorkflowTaskPane()
        {
            base.Initialize(false);
            this.InitializeComponent();
            this.ctrEditParameters.IsJobPresent = true;
            this.InitializeUserControls();
        }

        #region Private Methods

        /// <summary>
        /// Initializes the user controls.
        /// </summary>
        private void InitializeUserControls()
        {
            this.ControllerInstance.OnDocumentClosing += new EventHandler<BaseEventArgs>(Controller_OnDocumentClosing);
            this.ctrSelectRerunWorkflow.OnSelectedAssociationChanged += new EventHandler<SelectedJobChangedEventArgs>(ctrSelectRerunWorkflow_OnSelectedWorkflowAssociationChanged);
            this.ctrSelectRerunWorkflow.OnAssociationListUpdated += new EventHandler<JobsUpdatedEventArgs>(ctrSelectRerunWorkflow_OnAssociationListUpdated);

            this.ctrSelectRerunWorkflow.Initialize(true);
            this.ctrServerSettings.Initialize(true);
        }

        /// <summary>
        /// Validates the before rerun.
        /// </summary>
        /// <returns>bool</returns>
        private bool ValidateBeforeRerun()
        {
            if (this.workflow == null)
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoAssociationSelected"));
                return false;
            }

            if (this.ControllerInstance.Server == null || this.ControllerInstance.Server.CurrentTridentConnection == null)
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoConnection"));
                return false;
            }

            if (!this.ctrServerSettings.ValidateSelection())
            {
                return false;
            }

            if (this.ControllerInstance.Server.IsWorkflowDeleted(this.workflow.Id))
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("WorkflowDeleted"));
                return false;
            }

            if (this.ControllerInstance.Server.IsWorkflowIntractive(this.workflow.Id) &&
                !this.ControllerInstance.Server.IsMachineIntractive(this.ctrServerSettings.MachineName))
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("WorkflowIntractiveMachineNot"));
            }

            if (this.ControllerInstance.IsTransactionActive(DocumentKey))
            {
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("JobExecutionInProgress"), MessageBoxIcon.Warning);
                return false;
            }

            if (this.ctrEditParameters.ValidateParameters(this.workflow) == false)
            {
                this.tabctrActivity.SelectedIndex = 0;
                MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("IncorrectInput"), MessageBoxIcon.Warning);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Submits the rerun job.
        /// </summary>
        /// <param name="rerunParam">The rerun param.</param>
        private void SubmitRerunJob(object rerunParam)
        {
            try
            {
                this.jobId = ControllerInstance.SumbitJob(this.Server, rerunParam as RerunWorkflowInformation);                
                if (this.jobId != Guid.Empty)
                {
                    WorkflowAssociation association = this.ctrSelectRerunWorkflow.SelectedAssociation;
                    association.RerunJob = new RerunJob();
                    association.RerunJob.Job.Id = this.jobId;
                    association.RerunJob.Connection = this.Server.CurrentTridentConnection;
                }
                else
                {
                    this.isError = true;
                }
            }
            catch (DatabaseException ex)
            {
                this.isError = true;
                exceptionMessage = ex.Message;
            }
            catch (NoRecordFoundException)
            {
                this.isError = true;
                exceptionMessage = string.Format(ResourceFetcher.GetString("NoWorkflowFound"), workflow.Id);
            }
            catch (ThreadAbortException)
            {
                this.isError = true;
            }
            catch (Exception ex)
            {
                this.isError = true;
                if(ex.Message.CompareIgnoreCase("Unknown web service failure"))
                {
                    return;
                }

                this.BeginInvoke(new ShowMessageDelegate(MessageBoxHelper.ShowException), new object[] { ex, this});
            }
        }

        /// <summary>
        /// Befores the rerun begin.
        /// </summary>
        private void BeforeRerunBegin()
        {
            this.exceptionMessage = string.Empty;
            this.isCanceled = false;
            this.isError = false;
            this.btnRerun.Enabled = false;
            this.btnCancel.Enabled = true;
            this.splitContainerTop.Enabled = false;
            this.ControllerInstance.BeginModelTransaction(DocumentKey);
            this.jobName = this.ctrServerSettings.JobName;
            this.toolStriplblStatus.Text = "Rerun started...";
            
        }

        /// <summary>
        /// Afters the rerun end.
        /// </summary>
        private void AfterRerunBegin()
        {
            this.btnRerun.Enabled = true;
            this.btnCancel.Enabled = false;
            this.splitContainerTop.Enabled = true;

            if (this.isCanceled)
            {
                this.ControllerInstance.RollbackModelTransaction(DocumentKey);
                this.toolStriplblStatus.Text = "Rerun workflow cancelled by user";
            }
            else if (this.isError)
            {
                this.ControllerInstance.RollbackModelTransaction(DocumentKey);
                this.toolStriplblStatus.Text = "Rerun workflow failed!";
                if (exceptionMessage.Length != 0)
                {
                    MessageBoxHelper.ShowDefault(exceptionMessage, MessageBoxIcon.Warning);
                }
            }
            else
            {
                this.AfterRerunLanuched();
            }

        }

        /// <summary>
        /// Afters the rerun lanuched.
        /// </summary>
        private void AfterRerunLanuched()
        {
            this.exceptionMessage = string.Empty;
            this.isCanceled = false;
            this.isError = false;
            this.btnRerun.Enabled = false;
            this.btnCancel.Enabled = true;
            this.splitContainerTop.Enabled = false;
            this.isRerunLaunched = true;
            this.ControllerInstance.CommitModelTransaction(DocumentKey);
            this.ControllerInstance.BeginModelTransaction(DocumentKey);
            this.ControllerInstance.RerunCompleted(DocumentKey, this.ctrSelectRerunWorkflow.SelectedAssociation);            
            this.progressTimer.Start();
        }

        /// <summary>
        /// Afters the rerun end.
        /// </summary>
        private void AfterRerunEnd()
        {
            this.btnRerun.Enabled = true;
            this.btnCancel.Enabled = false;
            this.splitContainerTop.Enabled = true;
            this.isRerunLaunched = false;
            this.jobId = Guid.Empty;
            if (this.isError || this.isCanceled)
            {
                this.ControllerInstance.RollbackModelTransaction(DocumentKey);
            }
            else
            {
                this.ControllerInstance.CommitModelTransaction(DocumentKey);
            }
        }       

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the OnAssociationListUpdated event of the ctrSelectRerunWorkflow control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.JobsUpdatedEventArgs"/> instance containing the event data.</param>
        private void ctrSelectRerunWorkflow_OnAssociationListUpdated(object sender, JobsUpdatedEventArgs e)
        {
            this.btnRerun.Enabled = e.Count > 0;
            if (e.Count == 0)
            {
                this.ctrWorkflowActivities.Refresh(null);
                this.ctrEditParameters.Refresh(null, null);
            }
        }

        /// <summary>
        /// CTRs the select rerun workflow_ on selected workflow association changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        private void ctrSelectRerunWorkflow_OnSelectedWorkflowAssociationChanged(object sender, SelectedJobChangedEventArgs e)
        {
            this.workflow = e.Workflow;
            this.job = e.Job == null ? null : e.Job as WorkflowJob;
            this.ctrServerSettings.Reset();
            this.ctrWorkflowActivities.Refresh(this.workflow);
            this.ctrEditParameters.Refresh(this.workflow, this.job);
        }

        /// <summary>
        /// Handles the Click event of the btnRerun control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnRerun_Click(object sender, EventArgs e)
        {
            if (!this.ValidateBeforeRerun())
            {
                return;
            }

            string machine = this.ctrServerSettings.MachineName;
            string jobName = this.ctrServerSettings.JobName;
            this.Server = ControllerInstance.ServerBroker.GetVerifiedServer(this.ctrServerSettings.SelectedConnection);
            if (this.Server != null)
            {
                RerunWorkflowInformation rerunParam = new RerunWorkflowInformation(
                    jobName,
                    machine,
                    this.workflow,
                    this.ctrEditParameters.Parameters);

                this.BeforeRerunBegin();
                this.asyncRerunJob = new Thread(new ParameterizedThreadStart(this.SubmitRerunJob));
                this.asyncRerunJob.Name = "Submit Job";
                this.asyncRerunJob.SetApartmentState(ApartmentState.STA);
                this.asyncRerunJob.Start(rerunParam);
                this.progressTimer.Start();
            }
        }

        /// <summary>
        /// Handles the Tick event of the progressTimer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void progressTimer_Tick(object sender, EventArgs e)
        {
            if (!this.isRerunLaunched)
            {
                if (!this.asyncRerunJob.IsAlive)
                {
                    this.progressTimer.Stop();
                    this.AfterRerunBegin();
                }
            }
            else
            {
                bool isRunning = false;
                string status = string.Empty;

                if (this.jobId != Guid.Empty && !this.isCanceled && !this.isError)
                {
                    try
                    {
                        status = this.Server.JobState(jobId, out isRunning);
                    }
                    catch (ConnectionFailedException)
                    {
                        this.isError = true;
                        this.progressTimer.Stop();
                        MessageBoxHelper.ShowDefault(ResourceFetcher.GetString("NoConnection"), MessageBoxIcon.Error);
                    }
                    catch (Exception ex)
                    {
                        this.isError = true;
                        this.progressTimer.Stop();
                        MessageBoxHelper.ShowException(ex, null);
                    }
                }

                if (!isRunning || this.isError || this.isCanceled)
                {
                    this.progressTimer.Stop();
                    this.AfterRerunEnd();

                    if (this.isError)
                    {
                        status = "failed!";
                    }

                    string displayStatus = jobName + " " + status + "!";
                    this.toolStriplblStatus.Text = displayStatus;
                    this.toolStriplblStatus.ToolTipText = displayStatus;
                }
                else
                {
                    string displayStatus = jobName + " " + status + "...";
                    this.toolStriplblStatus.Text = displayStatus; 
                    this.toolStriplblStatus.ToolTipText = displayStatus;
                }
            }
        }

        /// <summary>
        /// Handles the Click event of the btnCancel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (this.asyncRerunJob != null)
            {
                    this.isCanceled = true;
                    this.toolStriplblStatus.Text = "Cancelling rerun workflow...";
                    this.asyncRerunJob.CancelThread();                    
            }

            if (this.jobId != Guid.Empty)
            {
                this.isCanceled = false;
                this.Server.TerminateJob(jobId);
                this.btnCancel.Enabled = false;
            }            
        }

        /// <summary>
        /// Handles the OnDocumentClosing event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.BaseEventArgs"/> instance containing the event data.</param>
        private void Controller_OnDocumentClosing(object sender, BaseEventArgs e)
        {
            if (e.DocumentKey == DocumentKey)
            {
                this.isCanceled = true;
                this.asyncRerunJob.CancelThread();
            }
        }

        #endregion

        #region ITaskPaneControl Members

        /// <summary>
        /// Sets the dock position.
        /// </summary>
        /// <param name="dockPosition">The dock position.</param>
        public void SetDockPosition(TaskPaneDockPositions dockPosition)
        {
            try
            {
                if ((dockPosition & (TaskPaneDockPositions.Left | TaskPaneDockPositions.Right | TaskPaneDockPositions.Floating)) != 0)
                {
                    this.SetVertical(true);
                }
                else
                {
                    this.SetVertical(false);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Sets the vertical.
        /// </summary>
        /// <param name="controlIsVertical">if set to <c>true</c> [control is vertical].</param>
        private void SetVertical(bool controlIsVertical)
        {
            this.SuspendLayout();

            int initialSplitterDistance = controlIsVertical ==
                true ? this.splitContainerTop.MinimumSize.Height : this.splitContainerTop.MinimumSize.Width;

            this.splitContainerBottom.SplitterDistance = initialSplitterDistance;
            this.splitContainerMiddle.SplitterDistance = initialSplitterDistance;
            this.splitContainerTop.SplitterDistance = initialSplitterDistance;

            Orientation orientation = controlIsVertical ? Orientation.Horizontal : Orientation.Vertical;

            this.splitContainerTop.Orientation = orientation;
            this.splitContainerMiddle.Orientation = orientation;
            this.splitContainerBottom.Orientation = orientation;

            if (controlIsVertical)
            {
                this.splitContainerComplete.SplitterDistance = (int)(this.Height * 0.88);
                this.splitContainerTop.SplitterDistance = (int)(this.Height * 0.25);
                this.splitContainerMiddle.SplitterDistance = (int)(this.Height * 0.44);
                this.splitContainerBottom.SplitterDistance = (int)(this.Height * 0.15);
            }
            else
            {
                this.splitContainerComplete.FixedPanel = FixedPanel.None;
                int splitterDistance = this.Width / 3;
                this.splitContainerComplete.SplitterDistance = (int)(this.Height * 0.85);
                this.splitContainerTop.SplitterDistance = splitterDistance;
                this.splitContainerMiddle.SplitterDistance = splitterDistance;
                this.splitContainerBottom.SplitterDistance = (int)(this.Width * 0.27);
            }

            this.ResumeLayout();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by the <see cref="T:System.ComponentModel.Component"/>.
        /// </summary>
        void IDisposable.Dispose()
        {
            this.Disposing(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the specified the privately held resources.
        /// </summary>
        /// <param name="disposing">if set to <c>true</c> disposes resources.</param>
        private void Disposing(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    this.ctrSelectRerunWorkflow.OnAssociationListUpdated -= new EventHandler<JobsUpdatedEventArgs>(ctrSelectRerunWorkflow_OnAssociationListUpdated);
                    this.ctrSelectRerunWorkflow.OnSelectedAssociationChanged -= new EventHandler<SelectedJobChangedEventArgs>(ctrSelectRerunWorkflow_OnSelectedWorkflowAssociationChanged);
                    this.ControllerInstance.OnDocumentClosing -= new EventHandler<BaseEventArgs>(Controller_OnDocumentClosing);
                    this.ctrServerSettings.UnregisterEventHandlers();
                    base.Disposing();
                }
            }

            this.disposed = true;
        }

        #endregion
    }
}
